<!--
 事件流程图

 对外接口;checkLayout  校准流够

 @Author: zhanbh
 @Date: 2021-04-11
-->
<template>
  <div class="event-flow">
    <event-node
      v-for="(item, index) of eventList"
      :key="'event' + index"
      :ref="`event-node-${index}`"
      :time="item.sj"
      :zlsjlx="item.zlsjlx"
      :status="+item.zlsjlx === 1 ? item.qszt : item.fkzt"
      v-bind="nodesInfo[index] || {}"
      :style="visibilityStyle"
    >
      <template>
        <slot :data="item"></slot>
      </template>
    </event-node>
  </div>
</template>

<script>
import EventNode from './event-node';
import componentResize from './component-resize.js';

export default {
  name: 'EventFlow',
  inheritAttrs: false,
  mixins: [componentResize],
  components: {
    EventNode
  },
  props: {
    eventList: {
      type: Array,
      default() {
        return [];
      }
    }
  },
  data() {
    return {
      nodesInfo: [], // 节点位置状态列表
      resizeDebounce: 10 // 防抖时间间隔
    };
  },
  computed: {
    // TODO 少于两行时 需要优化
    // 节点可视样式，如果还没重新计算好布局，就不展示节点，避免布局混乱的页面影响视觉
    visibilityStyle() {
      return this.nodesInfo.length > 0 ? '' : '';
    }
  },
  watch: {
    // 数据改变，重新校准流布局
    eventList() {
      this.checkLayout();
    }
  },
  methods: {
    /**
     * 获取原始的节点位置信息
     */
    getPositions() {
      const vm = this;

      let { eventList } = vm;
      let positions = [];

      for (let i = 0; i < eventList.length; i++) {
        let $node = vm.$refs[`event-node-${i}`];

        if ($node && $node[0].$el) {
          let { offsetTop, offsetLeft } = $node[0].$el;
          positions[i] = { top: offsetTop, left: offsetLeft };
        } else {
          break;
        }
      }

      return positions;
    },

    /**
     * 位置信息分层
     * @param positions 位置信息
     **/
    split2Rows(positions) {
      let rowList = [];
      let rowTop = Number.NEGATIVE_INFINITY;
      let row = [];
      let isOdd = true; // 是否是奇数层

      positions.forEach(({ top, left }, index) => {
        if (top > rowTop + 10) {
          // 允许10px误差
          rowTop = top;
          row = [];
          rowList.push(row);

          isOdd = rowList.length % 2 !== 0;
        }

        let item = {
          top,
          left,
          index,
          isOdd
        };

        // 偶数层需要反转顺序
        isOdd ? row.push(item) : row.unshift(item);
      });

      return rowList;
    },

    /**
     * 更新节点信息列表，根据节点位置信息转化
     * @param rowList 位置的层信息
     **/
    updateNodesInfo(rowList) {
      // 超过两行才需要计算
      if (rowList.length < 2) {
        return [];
      }

      const vm = this;
      let { eventList } = vm;
      let elWidth = vm.$el.clientWidth;

      // 生成节点位置状态信息
      let nodesInfo = [];
      let order = 0;

      for (let row of rowList) {
        let isLastRow = rowList[rowList.length - 1] === row;
        let isOdd = true;

        for (let item of row) {
          order++;
          let isStart = item === row[0];
          let isEnd = item === row[row.length - 1];
          let isFirst = item.index === 0;
          let isLast = item.index === eventList.length - 1;
          let isCorner = (isStart || isEnd) && !isFirst && !(isLast && row.length > 1);
          isOdd = item.isOdd;

          nodesInfo[item.index] = {
            isFirst,
            isLast,
            isLastRow,
            isOddRow: item.isOdd,
            isCorner,
            isCornerStart: isCorner && row.length > 1 && (item.isOdd ? isEnd : isStart),
            flexOrder: order
          };
        }

        // 计算非最后一行的奇数行最后一个节点的扩展宽度 防止位置调换后前一行容得下
        // 因为偶数行会反转顺序
        if (!isLastRow && isOdd) {
          let last = row[row.length - 1];
          last = last.isOdd ? last : row[0];

          nodesInfo[last.index].flexWidth = elWidth - last.left - 32; // 留32px，防止扩展过头导致换行
        }
      }

      vm.nodesInfo = nodesInfo;

      return nodesInfo;
    },

    /**
     * 处理div宽度改变
     */
    handleResize() {
      this.checkLayout();
    },

    // 对外接口
    /**
     * 校对工作流布局
     */
    checkLayout() {
      const vm = this;
      vm.nodesInfo = [];

      vm.$nextTick(() => {
        vm.updateNodesInfo(vm.split2Rows(vm.getPositions()));
      });
    }
  },
  mounted() {
    this.checkLayout();
  }
};
</script>
<style scoped lang="scss">
.event-flow {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  position: relative;

  &__last {
    flex-grow: 1;
    flex-shrink: 1;
    width: 22rem;
  }
}
</style>
